use std::sync::mpsc::Sender;

use egui::{Frame, Layout, Ui, Vec2};
use serde::{Deserialize, Serialize};
use thunderdome::Arena;

use QcCore::ecs::graph::Graph;
use QcMacros::Control;

use QcTools::message::messageSender::MessageSender;
use QcWindowing::{CursorIcon, Window};

use crate::{
    core::{context::UiContext, layer::Layer},
    message::UiMessage,
};

use super::{Canvas, ToUi, UiNode, UiNodeTrait};

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum FlexDirection {
    Row,
    Column,
    RowReverse,
    ColumnReverse,
}

#[derive(Control, Serialize, Deserialize, Debug)]
pub struct Panel {
    pub widget: Widget,
    pub orientation: FlexDirection,
    pub spacing: f32,
}

#[typetag::serde]
impl UiNodeTrait for Panel {
    fn renderFrame(&self, ctx: &mut UiContext) -> egui::Frame {
        let frame = Frame::none()
            .fill(self.background)
            .inner_margin(self.padding)
            .outer_margin(self.margin);

        frame
    }

    fn renderInner(&mut self, ctx: &mut UiContext) {
        let UiContext {
            ui,
            sender,
            canvas_ptr,
        } = ctx;

        let canvas = unsafe { &mut **canvas_ptr };

        if self.width != 0.0 && self.height != 0.0 {
            if self.width == f32::INFINITY {
                ui.set_width(ui.available_width());
            } else {
                ui.set_width(self.width);
            }
            if self.height == f32::INFINITY {
                ui.set_height(ui.available_height());
            } else {
                ui.set_height(self.height);
            }
        }

        ui.scope(|ui| {
            ui.style_mut().spacing.item_spacing = Vec2::new(self.spacing, self.spacing);

            let layout = match self.orientation {
                FlexDirection::Column => Layout::top_down(self.align.x()),
                FlexDirection::ColumnReverse => Layout::bottom_up(self.align.x()),
                FlexDirection::Row => Layout::left_to_right(self.align.y()),
                FlexDirection::RowReverse => Layout::right_to_left(self.align.y()),
            };

            let res = ui.with_layout(layout, |ui| {
                for index in self.children.iter() {
                    let node = &mut canvas[*index];
                    node.render(&mut UiContext::new(ui, sender).with_canvas_ptr(*canvas_ptr));
                }
            });
        });
    }
}

impl Default for Panel {
    fn default() -> Self {
        Self {
            widget: Widget::default()
                .with_name("Panel")
                .with_width(200.)
                .with_height(200.),
            orientation: FlexDirection::Row,
            spacing: 0.,
        }
    }
}

impl Panel {
    pub fn new(widget: Widget) -> Self {
        Self {
            widget,
            ..Default::default()
        }
    }

    pub fn with_orientation(mut self, orientation: FlexDirection) -> Self {
        self.orientation = orientation;
        self
    }

    pub fn with_spacing(mut self, spacing: f32) -> Self {
        self.spacing = spacing;
        self
    }

    pub fn with_children(mut self, children: Vec<Index>, canvas: &mut Canvas) -> Self {
        for index in children {
            self.children.push(index);

            canvas[index].get_widget_mut().set_layer(Layer::Panel);
        }
        self
    }

    pub fn build(self, canvas: &mut Canvas) -> Index {
        let panel = canvas.add_child(UiNode::Panel(self));

        let children = &canvas[panel].get_widget().children.clone();

        for index in children {
            let widget = canvas[*index].get_widget_mut();
            widget.set_parent(Some(panel));
        }

        panel
    }

    pub fn set_orientation(&mut self, mode: i32) {
        let orientation = match mode {
            0 => FlexDirection::Row,
            1 => FlexDirection::Column,
            2 => FlexDirection::RowReverse,
            3 => FlexDirection::ColumnReverse,
            _ => FlexDirection::Row,
        };
        self.orientation = orientation;
    }
}

impl ToUi for Panel {
    fn toUi(self) -> UiNode {
        UiNode::Panel(self)
    }
}
